package cn.iocoder.yudao.module.wecom.service.apiconfig;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.*;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
import cn.iocoder.yudao.module.system.api.extend.ExtendApi;
import cn.iocoder.yudao.module.system.api.tenant.TenantApi;
import cn.iocoder.yudao.module.system.api.tenant.dto.TenantSaveReqDTO;
import cn.iocoder.yudao.module.wecom.controller.admin.msgrecord.vo.WeMsgRecordSaveReqVO;
import cn.iocoder.yudao.module.wecom.dal.dataobject.chatgroup.WeChatGroupDO;
import cn.iocoder.yudao.module.wecom.dal.dataobject.providerconfig.WeComProviderConfigDO;
import cn.iocoder.yudao.module.wecom.dal.dataobject.user.WeUserDO;
import cn.iocoder.yudao.module.wecom.dal.mysql.chatgroup.WeChatGroupMapper;
import cn.iocoder.yudao.module.wecom.dal.mysql.customer.WeCustomerMapper;
import cn.iocoder.yudao.module.wecom.dal.mysql.user.WeUserMapper;
import cn.iocoder.yudao.module.wecom.dal.redis.RedisKeyConstants;
import cn.iocoder.yudao.module.wecom.http.WeApi;
import cn.iocoder.yudao.module.wecom.service.chatmsgrecord.WeChatMsgRecordService;
import cn.iocoder.yudao.module.wecom.service.msgrecord.WeMsgRecordService;
import cn.iocoder.yudao.module.wecom.service.providerconfig.WeComProviderConfigService;
import cn.iocoder.yudao.module.wecom.service.user.WeUserService;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.TimeUnit;

import cn.iocoder.yudao.module.wecom.controller.admin.apiconfig.vo.*;
import cn.iocoder.yudao.module.wecom.dal.dataobject.apiconfig.WeComApiConfigDO;

import cn.iocoder.yudao.module.wecom.dal.mysql.apiconfig.WeComApiConfigMapper;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.wecom.enums.ErrorCodeConstants.*;

/**
 * 企业参数配置 Service 实现类
 *
 * @author 数创万维
 */
@Service
@Slf4j
public class WeComApiConfigServiceImpl implements WeComApiConfigService {

    @Resource
    private WeComApiConfigMapper weComApiConfigMapper;

    @Resource
    private WeComProviderConfigService weComProviderConfigService;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Resource
    private WeMsgRecordService weMsgRecordService;

    @Resource
    private WeChatMsgRecordService weChatMsgRecordService;

    @Resource
    private WeChatGroupMapper weChatGroupMapper;

    @Resource
    private WeCustomerMapper weCustomerMapper;

    @Resource
    private WeUserMapper weUserMapper;

    @Resource
    private TenantApi tenantApi;

    @Resource
    private ExtendApi extendApi;

    @Resource
    private WeUserService weUserService;

    private static final String CORP_AUTH_REDIRECT_URI = "";


    /**
     * 获取企业授权码
     * 1. 获取与授权码
     * SUITE_ACCESS_TOKEN
     * 2. 拼接授权链接
     * https://open.work.weixin.qq.com/3rdapp/install?suite_id=SUITE_ID&pre_auth_code=PRE_AUTH_CODE&redirect_uri=REDIRECT_URI&state=STATE
     *
     * @return
     * @author PetePower
     * @since 2024-07-17
     */
    @Override
    public String getCorpAuthUrl() {
//        log.info("获取企业授权链接： ======================================= ");
        WeComProviderConfigDO providerConfig = weComProviderConfigService.getProviderConfig();
        if (StrUtil.isAllNotEmpty(providerConfig.getSuiteAccessToken(), providerConfig.getSuiteId())) {
            String preAuthCodeStr = HttpUtil.get("" + providerConfig.getSuiteAccessToken());
            JSONObject preAuthCodeJson = JSONUtil.parseObj(preAuthCodeStr);
            Map<String, Object> params = new HashMap<>();
            params.put("suite_id", providerConfig.getSuiteId());
            params.put("pre_auth_code", preAuthCodeJson.getStr("pre_auth_code"));
            params.put("redirect_uri", CORP_AUTH_REDIRECT_URI);
            params.put("state", TenantContextHolder.getTenantId());
//            log.info("获取企业授权链接： {}", StrUtil.concat(true, "https://open.work.weixin.qq.com/3rdapp/install?", HttpUtil.toParams(params, CharsetUtil.CHARSET_UTF_8, true)));
            return StrUtil.concat(true, "?", HttpUtil.toParams(params, CharsetUtil.CHARSET_UTF_8, true));
        }
        return null;
    }

    @Override
    public String getContactAuthUrl() {
        // 先检查锁， 获取当前锁和当前企业id是否一致， 如果不一致，保持锁并返回异常； 如果一致， 则更新锁的时间
        String lockTenantId = stringRedisTemplate.opsForValue().get("CONTACT_AUTH_LOCK");
        if (StrUtil.isNotEmpty(lockTenantId)) {
            if (ObjectUtil.notEqual(lockTenantId, TenantContextHolder.getTenantIdStr())) {
                throw exception(API_CONTACT_AUTH_LOCK);
            }
        }
        stringRedisTemplate.opsForValue().set("CONTACT_AUTH_LOCK", TenantContextHolder.getTenantIdStr(), 10, TimeUnit.MINUTES);
        return "https://scrm.jzsaas.com/qrcode.jpeg";
    }

    @Override
    public String getAcqAuthUrl() {
        // 先检查锁， 获取当前锁和当前企业id是否一致， 如果不一致，保持锁并返回异常； 如果一致， 则更新锁的时间
        String lockTenantId = stringRedisTemplate.opsForValue().get("ACQ_AUTH_LOCK");
        if (StrUtil.isNotEmpty(lockTenantId)) {
            if (ObjectUtil.notEqual(lockTenantId, TenantContextHolder.getTenantIdStr())) {
                throw exception(API_CONTACT_AUTH_LOCK);
            }
        }
        stringRedisTemplate.opsForValue().set("ACQ_AUTH_LOCK", TenantContextHolder.getTenantIdStr(), 10, TimeUnit.MINUTES);
        return "https://scrm.jzsaas.com/acq_qrcode.png";
    }

    /**
     * 获取企业永久授权码
     * 最后更新：2023/12/12
     * 该API用于使用临时授权码换取授权方的永久授权码，并换取授权信息、企业access_token，临时授权码一次有效。
     * <p>
     * 请求方式：POST（HTTPS）
     * 请求地址： https://qyapi.weixin.qq.com/cgi-bin/service/get_permanent_code?suite_access_token=SUITE_ACCESS_TOKEN
     * <p>
     * 请求包体：
     * <p>
     * {
     * "auth_code": "auth_code_value"
     * }
     *
     * @param state
     * @param code
     * @author PetePower
     * @since 2024-07-17
     */
    @Override
    public void bindCorp(String state, String code) {

        // 获取永久授权码
        WeComProviderConfigDO providerConfig = weComProviderConfigService.getProviderConfig();
        // 授权第三方应用权限
//        setAccessTokenAuth(code, providerConfig.getSuiteAccessToken());
        Map<String, Object> params = new HashMap<>();
        params.put("auth_code", code);

        WeApi weApi = new WeApi(providerConfig.getSuiteAccessToken()).body(params);
        JSONObject authInfo = weApi.getPermanentCode();
        // 继续在这里进行下一步的逻辑， 如果state = -1 ，表示从应用市场授权来的， 那么在这里直接注册企业和账号
        JSONObject authCorpInfo = authInfo.getJSONObject("auth_corp_info");
        JSONObject agentAuthInfo = authInfo.getJSONObject("auth_info");
        JSONArray agentArray = agentAuthInfo.getJSONArray("agent");
        JSONObject authUserInfo = authInfo.getJSONObject("auth_user_info");

        if (StrUtil.equals("-1", state)) {
            // 1. 执行企业注册代码
            // 2. 执行创建用户代码
            // 3. 搞完返回tenantId
            // 接着进行下一步
            String name = authCorpInfo.getStr("corp_name");

            Long tenantId = tenantApi.getTenantIdByName(name);
            if (ObjUtil.isNull(tenantId)) {
                String username = extendApi.pinyin(name);
                TenantSaveReqDTO tenantSaveReqDTO = new TenantSaveReqDTO();
                tenantSaveReqDTO.setAuthType(1);
                tenantSaveReqDTO.setName(name);
                tenantSaveReqDTO.setContactName(name);
                tenantSaveReqDTO.setStatus(CommonStatusEnum.ENABLE.getStatus());
                tenantSaveReqDTO.setPackageId(2L);
                tenantSaveReqDTO.setUsername(username);
                tenantSaveReqDTO.setExpireTime(DateUtil.offsetDay(new Date(), 7).toLocalDateTime());
                Map<String, Long> tenantInfo = tenantApi.createTenantApi(tenantSaveReqDTO);
                tenantId = tenantInfo.get("tenantId");
                //绑定企微管理员
                TenantUtils.execute(Long.parseLong(state), () -> {
                    weUserService.bindUser(authUserInfo.getStr("userid"), authUserInfo.getStr("open_userid"), tenantInfo.get("userId"));
                });
            }
            state = String.valueOf(tenantId);
        }
        TenantUtils.execute(Long.parseLong(state), () -> {
            WeComApiConfigDO apiConfig = getWeComApiConfig();
            if (Objects.isNull(apiConfig)) {
                apiConfig = new WeComApiConfigDO();
            }
            apiConfig.setCorpId(authCorpInfo.getStr("corpid"));
            apiConfig.setCorpName(authCorpInfo.getStr("corp_name"));
            apiConfig.setCorpFullName(authCorpInfo.getStr("corp_full_name"));
            apiConfig.setCorpUserMax(authCorpInfo.getInt("corp_user_max"));
            apiConfig.setCorpSubjectType(authCorpInfo.getInt("subject_type"));
            apiConfig.setCorpLogoUrl(authCorpInfo.getStr("corp_square_logo_url"));
            apiConfig.setAccessToken(authInfo.getStr("access_token"));
            apiConfig.setAccessTokenExpireIn(authInfo.getInt("expires_in"));
            apiConfig.setPermanentCode(authInfo.getStr("permanent_code"));

            // apiConfig 会话存档默认保存天数：7
            apiConfig.setMsgSaveDay(7);
            if (Objects.nonNull(agentArray) && Objects.nonNull(agentArray.get(0))) {
                JSONObject agentInfo = JSONUtil.parseObj(agentArray.get(0));
                apiConfig.setAgentId(agentInfo.getStr("agentid"));
            }

            // 去获取企业access_token
            updateAccessToken(apiConfig, providerConfig.getSuiteAccessToken());

            weComApiConfigMapper.insertOrUpdate(apiConfig);
        });
    }

    @Override
    public void bindContactAuth(String state, String code) {
        // 从redis中获取10分钟内的缓存，如果没有缓存，则提示失败
        String lockTenantId = stringRedisTemplate.opsForValue().get("CONTACT_AUTH_LOCK");
        if (StrUtil.isEmpty(lockTenantId)) {
            throw exception(API_CONTACT_AUTH_TIMEOUT);
        }
        TenantUtils.execute(Long.parseLong(lockTenantId), () -> {
            WeComApiConfigDO apiConfig = getWeComApiConfig();
            if (Objects.isNull(apiConfig)) {
                return;
            }
            // 获取永久授权码
            WeComProviderConfigDO providerConfig = weComProviderConfigService.getProviderConfig();
            if (StrUtil.isEmpty(providerConfig.getContactSuiteAccessToken())) {
                return;
            }
            Map<String, Object> params = new HashMap<>();
            params.put("auth_code", code);
            WeApi weApi = new WeApi(providerConfig.getContactSuiteAccessToken()).body(params);
            JSONObject authInfo = weApi.getPermanentCode();
            apiConfig.setContactPermanentCode(authInfo.getStr("permanent_code"));
            apiConfig.setContactAuthStatus(1);
            updateContactAccessToken(apiConfig, providerConfig.getContactSuiteAccessToken());

            weComApiConfigMapper.updateById(apiConfig);

            stringRedisTemplate.delete("CONTACT_AUTH_LOCK");
        });
    }

    @Override
    public void bindAcqAuth(String state, String code) {
        // 从redis中获取10分钟内的缓存，如果没有缓存，则提示失败
        String lockTenantId = stringRedisTemplate.opsForValue().get("ACQ_AUTH_LOCK");
        if (StrUtil.isEmpty(lockTenantId)) {
            throw exception(API_CONTACT_AUTH_TIMEOUT);
        }
        TenantUtils.execute(Long.parseLong(lockTenantId), () -> {
            WeComApiConfigDO apiConfig = getWeComApiConfig();
            if (Objects.isNull(apiConfig)) {
                return;
            }
            // 获取永久授权码
            WeComProviderConfigDO providerConfig = weComProviderConfigService.getProviderConfig();
            if (StrUtil.isEmpty(providerConfig.getAcqSuiteAccessToken())) {
                return;
            }
            Map<String, Object> params = new HashMap<>();
            params.put("auth_code", code);
            WeApi weApi = new WeApi(providerConfig.getAcqSuiteAccessToken()).body(params);
            JSONObject authInfo = weApi.getPermanentCode();
            apiConfig.setAcqPermanentCode(authInfo.getStr("permanent_code"));
            apiConfig.setAcqAuthStatus(1);
            updateAcqAccessToken(apiConfig, providerConfig.getAcqSuiteAccessToken());
            weComApiConfigMapper.updateById(apiConfig);

            stringRedisTemplate.delete("ACQ_AUTH_LOCK");
        });
    }

    @Override
    @TenantIgnore   // 忽略租户， 使用corpId 查找ApiConfig
    public void changeCustomerAcq(Map<String, Object> params) {
        String corpId = params.get("AuthCorpId").toString();
//        String suiteId = params.get("SuiteId").toString();
        String changeType = params.get("ChangeType").toString();
        // 企业使用量即将耗尽事件

        switch (changeType) {
            case "balance_low":
            case "balance_exhausted":
            case "balance_increased":
                WeComApiConfigDO apiConfig = weComApiConfigMapper.selectOne(WeComApiConfigDO::getCorpId, corpId);
                if (Objects.nonNull(apiConfig)) {
                    apiConfig.setAcqStatus(changeType.equals("balance_increased") ? "normal" : changeType);
                    weComApiConfigMapper.updateById(apiConfig);
                }
                break;

            default:
                log.info("监测到获客助手使用状态变更事件： {}", changeType);
                break;
        }
    }

    @Override
    public void bindDataAuth() {
        // 设置专区回调通知事件
        WeComApiConfigDO apiConfig = getWeComApiConfig();
        final String accessToken = apiConfig.getAccessToken();
        WeApi weApi = new WeApi(accessToken);

        JSONObject json1 = weApi.setDataReceiveCallback();
        if (Objects.equals(48002, json1.getInt("errcode"))) {
            throw exception(DATA_NO_ACCESS_TOKEN);
        }
        // 设置密钥
        JSONObject json2 = weApi.setDataPublicKey();
        if (Objects.equals(48002, json2.getInt("errcode"))) {
            throw exception(DATA_NO_ACCESS_TOKEN);
        }
        apiConfig.setDataAuthStatus(1);
        weComApiConfigMapper.updateById(apiConfig);
    }

    @Override
    @TenantIgnore
    public void handleMsgNotify(String corpId, String notifyId) {
        // 根据notify_id 去调用program, 得到Token
        WeComApiConfigDO apiConfig = weComApiConfigMapper.selectOne(WeComApiConfigDO::getCorpId, corpId);
        if (Objects.isNull(apiConfig)) {
            return;
        }
//        Map<String, LocalDateTime> weCustomerIdMap = new HashMap<>();
        Map<String, LocalDateTime> weUserIdMap = new HashMap<>();
        Map<String, LocalDateTime> weChatIdMap = new HashMap<>();
        TenantUtils.execute(apiConfig.getTenantId(), () -> {
            WeApi weApi = new WeApi(apiConfig.getAccessToken());
            JSONObject json1 = weApi.syncCallProgramGetCallbackData(notifyId);
            JSONObject responseData = json1.getJSONObject("response_data");
            String token = responseData.getStr("token");
            // 根据 Token 获取会话记录
            String queryCursor = apiConfig.getDataQueryCursor();
            JSONObject json2 = weApi.syncCallProgramSyncMsg(queryCursor, token);
            // 解析会话记录，保存到 mysql
            JSONObject responseData2 = json2.getJSONObject("response_data");
            apiConfig.setDataQueryCursor(responseData2.getStr("next_cursor"));
            weComApiConfigMapper.updateById(apiConfig);
            JSONArray records = responseData2.getJSONArray("msg_list");
            records.forEach(record -> {
                // !fixed 修复一个msg 重复推送的bug， lock 缓存 key 一天， 一天内消息不重复入库
                JSONObject recordData = JSONUtil.parseObj(record);
                String msgid = recordData.getStr("msgid");
                String msgidLock = stringRedisTemplate.opsForValue().get("WECOM_MSG_" + msgid);
                if (StrUtil.isEmpty(msgidLock)) {
                    stringRedisTemplate.opsForValue().set("WECOM_MSG_" + msgid, msgid);
                    stringRedisTemplate.expire("WECOM_MSG_" + msgid, 24, TimeUnit.HOURS);

                    // 1. 判断是否群聊
                    String chatid = recordData.getStr("chatid");

                    WeMsgRecordSaveReqVO saveReqVO = new WeMsgRecordSaveReqVO();
                    saveReqVO.setMsgid(recordData.getStr("msgid"));
                    saveReqVO.setSenderId(recordData.getJSONObject("sender").getStr("id"));
                    // 消息发送者身份类型。1：员工；2：外部联系人; 3：机器人
                    saveReqVO.setSenderType(recordData.getJSONObject("sender").getInt("type"));
                    saveReqVO.setMsgtype(recordData.getInt("msgtype"));
                    saveReqVO.setSendTime(LocalDateTimeUtil.of(recordData.getLong("send_time") * 1000));
                    saveReqVO.setSecureKey(weApi.decryptMsg(recordData.getJSONObject("service_encrypt_info").getStr("encrypted_secret_key")));
                    if (StrUtil.isNotEmpty(chatid)) {
                        saveReqVO.setChatid(chatid);
                        weChatMsgRecordService.createChatWeMsgRecord(saveReqVO);
                    } else {
                        JSONArray receiver_list = recordData.getJSONArray("receiver_list");
                        receiver_list.forEach(receiver -> {
                            JSONObject receiverData = JSONUtil.parseObj(receiver);
                            saveReqVO.setReceiverId(receiverData.getStr("id"));
                            // 消息接收者身份类型。1：员工；2：外部联系人; 3：机器人
                            saveReqVO.setReceiverType(receiverData.getInt("type"));
                            weMsgRecordService.createWeMsgRecord(saveReqVO);
                        });
                    }


                    // 群消息
                    if (StrUtil.isNotEmpty(chatid)) {
                        weChatIdMap.put(recordData.getStr("chatid"), saveReqVO.getSendTime());
                    } else {
                        // 员工给客户发的
                        if (Objects.equals(saveReqVO.getSenderType(), 1) && Objects.equals(saveReqVO.getReceiverType(), 2)) {
                            weUserIdMap.put(saveReqVO.getSenderId(), saveReqVO.getSendTime());
//                            weCustomerIdMap.put(saveReqVO.getReceiverId(), saveReqVO.getSendTime());
                        }
                        // 客户给员工发的
//                        if (Objects.equals(saveReqVO.getSenderType(), 2) && Objects.equals(saveReqVO.getReceiverType(), 1)) {
//                            weUserIdMap.put(saveReqVO.getReceiverId(), saveReqVO.getSendTime());
//                            weCustomerIdMap.put(saveReqVO.getSenderId(), saveReqVO.getSendTime());
//                        }
                    }
                }
//                weMsgRecordService.getWeMsgRecord()

            });
            if (MapUtil.isNotEmpty(weChatIdMap)) {
                for (String key : weChatIdMap.keySet()) {
                    weChatGroupMapper.update(new LambdaUpdateWrapper<WeChatGroupDO>()
                            .eq(WeChatGroupDO::getChatId, key)
                            .set(WeChatGroupDO::getLastChatTime, weChatIdMap.get(key))
                    );
                }
            }
            if (MapUtil.isNotEmpty(weUserIdMap)) {
                for (String key : weUserIdMap.keySet()) {
                    weUserMapper.update(new LambdaUpdateWrapper<WeUserDO>()
                            .eq(WeUserDO::getWeUserId, key)
                            .set(WeUserDO::getLastChatTime, weUserIdMap.get(key))
                    );
                }
            }
//            if (MapUtil.isNotEmpty(weCustomerIdMap)) {
//                for (String key : weCustomerIdMap.keySet()) {
//                    weCustomerMapper.update(new LambdaUpdateWrapper<WeCustomerDO>()
//                            .eq(WeCustomerDO::getExternalUserId, key)
//                            .set(WeCustomerDO::getLastChatTime, weCustomerIdMap.get(key))
//                    );
//                }
//            }
        });
    }


    @Override
    public WeComApiConfigDO getWeComApiConfig() {
        WeComApiConfigDO apiConfig = weComApiConfigMapper.selectOne(WeComApiConfigDO::getDeleted, 0);
        return apiConfig;

    }

    @Override
    public WeComApiConfigDO getWeComApiConfigByCorpId(String corpId) {
        return weComApiConfigMapper.selectOne(new LambdaQueryWrapperX<WeComApiConfigDO>().eq(WeComApiConfigDO::getCorpId, corpId));
    }

    @Override
    public String getWeComJsApiTicket() {
        String jsApiTicket = stringRedisTemplate.opsForValue().get(RedisKeyConstants.WE_JSAPI_TICKET + TenantContextHolder.getTenantId());
        if (StrUtil.isNotEmpty(jsApiTicket)) {
            return jsApiTicket;
        }
        WeComApiConfigDO apiConfig = getWeComApiConfig();
        if (Objects.isNull(apiConfig)) {
            throw exception(API_NO_ACCESS_TOKEN);
        }

        return getJsApiTicket(apiConfig.getAccessToken(), TenantContextHolder.getTenantId());
    }

    @Override
    public String getWeComJsApiTicketByCorpId(String corpId) {

        WeComApiConfigDO apiConfig = getWeComApiConfigByCorpId(corpId);
        if (Objects.isNull(apiConfig)) {
            throw exception(API_NO_ACCESS_TOKEN);
        }
        return getJsApiTicket(apiConfig.getAccessToken(), apiConfig.getTenantId());
    }

    public String getJsApiTicket(String accessToken, Long tenantId) {
        WeApi weApi = new WeApi(accessToken);
        JSONObject json1 = weApi.getJsApiTicket();
        String jsApiTicket = null;
        log.info("getJsApiTicket:=========== {}", json1);
        if (Objects.equals(json1.getInt("errcode"), 0)) {
            jsApiTicket = json1.getStr("ticket");
            Long expiresIn = json1.getLong("expires_in") - 10;
            stringRedisTemplate.opsForValue().set(RedisKeyConstants.WE_JSAPI_TICKET + tenantId, jsApiTicket, expiresIn, TimeUnit.SECONDS);
            return jsApiTicket;
        }
        return null;
    }

    @Override
    public WeComApiSignRespVO getWeComJsApiSignature(WeComApiSignReqVO reqVO) {
        String nonceStr = RandomUtil.randomNumbers(16);
        long timestamp = System.currentTimeMillis() / 1000;
        String jsApiTicket = StrUtil.isEmpty(reqVO.getCorpId()) ? getWeComJsApiTicket() : getWeComJsApiTicketByCorpId(reqVO.getCorpId());
        String signature = SecureUtil.sha1(String.format("jsapi_ticket=%s&noncestr=%s&timestamp=%s&url=%s",
                jsApiTicket, nonceStr, timestamp, reqVO.getUrl()));
        if (StrUtil.isNotEmpty(signature)) {
            return WeComApiSignRespVO.builder()
                    .jsapiTicket(jsApiTicket)
                    .nonceStr(nonceStr)
                    .signature(signature)
                    .timestamp(timestamp)
                    .url(reqVO.getUrl())
                    .build();
        }
        return null;
    }

    @Override
    public String getChatToolPageUrl() {
        WeComApiConfigDO apiConfig = getWeComApiConfig();
        if (Objects.nonNull(apiConfig)) {
            return "https://scrm.jzsaas.com/pageH5?corpid=" + apiConfig.getCorpId();
        }
        return null;
    }

    @Override
    public List<WeComApiConfigDO> getWeComApiConfigList() {
        return weComApiConfigMapper.selectList();
    }

    /**
     * 创建授权通知事件处理
     * 1. 企业用户扫码授权
     * 2. 通讯录权限授权（State 参数怎么解决？？？）
     *
     * @param suiteId
     * @param state
     * @param code
     * @author PetePower
     * @since 2024-07-23
     */
    @Override
    public void createAuth(String suiteId, String state, String code) {
        // 在这里判断，是应用授权还是通讯录授权
        WeComProviderConfigDO providerConfig = weComProviderConfigService.getProviderConfig();
        if (ObjectUtil.equal(providerConfig.getSuiteId(), suiteId)) {
            // 企业使用授权
            bindCorp(state, code);
        } else if (ObjectUtil.equal(providerConfig.getContactSuiteId(), suiteId)) {
            // 通讯录授权
            bindContactAuth(state, code);
        } else if (ObjectUtil.equal(providerConfig.getAcqSuiteId(), suiteId)) {
            // 获客助手授权
            bindAcqAuth(state, code);
        }
    }

    @Override
    @TenantIgnore
    public void cancelAuth(String suiteId, String corpId) {
        // 在这里判断，是应用授权还是通讯录授权
        WeComProviderConfigDO providerConfig = weComProviderConfigService.getProviderConfig();
        if (ObjectUtil.equal(providerConfig.getSuiteId(), suiteId)) {
            // 企业使用授权
            unbindCorpAuth(corpId);
        } else if (ObjectUtil.equal(providerConfig.getContactSuiteId(), suiteId)) {
            // 通讯录授权
            unbindContactAuth(corpId);
        } else if (ObjectUtil.equal(providerConfig.getAcqSuiteId(), suiteId)) {
            // 获客助手授权
            unbindAcqAuth(corpId);
        }
    }

    @Override
    public void unbindCorpAuth(String corpId) {
        weComApiConfigMapper.delete(WeComApiConfigDO::getCorpId, corpId);
    }

    @Override
    public void unbindContactAuth(String corpId) {
        weComApiConfigMapper.update(
                new LambdaUpdateWrapper<WeComApiConfigDO>().eq(WeComApiConfigDO::getCorpId, corpId)
                        .set(WeComApiConfigDO::getContactAuthStatus, 0)
                        .set(WeComApiConfigDO::getContactPermanentCode, null)
                        .set(WeComApiConfigDO::getContactAccessToken, null)
                        .set(WeComApiConfigDO::getContactTokenExpireIn, null)
        );
    }

    @Override
    public void unbindAcqAuth(String corpId) {
        weComApiConfigMapper.update(
                new LambdaUpdateWrapper<WeComApiConfigDO>().eq(WeComApiConfigDO::getCorpId, corpId)
                        .set(WeComApiConfigDO::getAcqAuthStatus, 0)
                        .set(WeComApiConfigDO::getAcqPermanentCode, null)
                        .set(WeComApiConfigDO::getAcqAccessToken, null)
                        .set(WeComApiConfigDO::getAcqTokenExpireIn, null)
        );
    }

    @Override
    public void saveMsgSaveDay(Integer day) {
        weComApiConfigMapper.update(new LambdaUpdateWrapper<WeComApiConfigDO>().set(WeComApiConfigDO::getMsgSaveDay, day));
    }

    @Scheduled(fixedRate = 60, timeUnit = TimeUnit.MINUTES)
    @TenantIgnore
    @Override
    public void refreshAccessToken() {
        String value = SpringUtil.getProperty("yudao.job.enable");
        if (!StrUtil.equals("true", value)) {
            return;
        }
        WeComProviderConfigDO providerConfig = weComProviderConfigService.getProviderConfig();
        List<WeComApiConfigDO> apiConfigList = getWeComApiConfigList();
//        WeComApiConfigDO apiConfigDO = weComApiConfigMapper.selectOne(WeComApiConfigDO::getTenantId, 1L);

        for (WeComApiConfigDO apiConfigDO : apiConfigList) {
            TenantUtils.execute(apiConfigDO.getTenantId(), () -> {
                updateAccessToken(apiConfigDO, providerConfig.getSuiteAccessToken());
                updateContactAccessToken(apiConfigDO, providerConfig.getContactSuiteAccessToken());
                updateAcqAccessToken(apiConfigDO, providerConfig.getAcqSuiteAccessToken());
                weComApiConfigMapper.updateById(apiConfigDO);
            });
        }
    }


    /**
     * 最后更新：2021/11/30
     * 第三方服务商在取得企业的永久授权码后，通过此接口可以获取到企业的access_token。
     * 获取后可通过通讯录、应用、消息等企业接口来运营这些应用。
     * <p>
     * 此处获得的企业access_token与企业获取access_token拿到的token，本质上是一样的，只不过获取方式不同。获取之后，就跟普通企业一样使用token调用API接口
     * 调用企业接口所需的access_token获取方法如下。
     * <p>
     * 请求方式：POST（HTTPS）
     * 请求地址： https://qyapi.weixin.qq.com/cgi-bin/service/get_corp_token?suite_access_token=SUITE_ACCESS_TOKEN
     * {
     * "auth_corpid": "auth_corpid_value",
     * "permanent_code": "code_value"
     * }
     *
     * @author PetePower
     * @since 2024-07-17
     */
    public void updateAccessToken(WeComApiConfigDO apiConfig, String suiteAccessToken) {
        if (StrUtil.isEmpty(suiteAccessToken)) {
            return;
        }
        String accessToken = stringRedisTemplate.opsForValue().get("weCom:api:accessToken:" + apiConfig.getCorpId());
        if (StrUtil.isNotEmpty(accessToken)) {
            return;
        }

        Map<String, Object> params = new HashMap<>();
        params.put("auth_corpid", apiConfig.getCorpId());
        params.put("permanent_code", apiConfig.getPermanentCode());
        String respStr = HttpUtil.createPost("https://qyapi.weixin.qq.com/cgi-bin/service/get_corp_token?suite_access_token=" + suiteAccessToken)
                .body(JSONUtil.toJsonStr(params))
                .execute().body();
        JSONObject accessInfo = JSONUtil.parseObj(respStr);
        log.debug("原apiConfig： >>>>> {}", apiConfig);
        log.info("获取企业凭证： REQ === {} ", params);
        log.info("获取企业凭证： RESP === {} ", accessInfo);
        apiConfig.setAccessToken(accessInfo.getStr("access_token"));
        apiConfig.setAccessTokenExpireIn(accessInfo.getInt("expires_in"));
//        stringRedisTemplate.opsForValue().set("weCom:api:accessToken:" + apiConfig.getCorpId(), suiteAccessToken, apiConfig.getAccessTokenExpireIn() - 10, TimeUnit.SECONDS);

    }

    public void updateAcqAccessToken(WeComApiConfigDO apiConfig, String suiteAccessToken) {
        if (StrUtil.isEmpty(suiteAccessToken) || apiConfig.getAcqAuthStatus() == 0) {
            return;
        }
        Map<String, Object> params = new HashMap<>();
        params.put("auth_corpid", apiConfig.getCorpId());
        params.put("permanent_code", apiConfig.getAcqPermanentCode());
        String respStr = HttpUtil.createPost("https://qyapi.weixin.qq.com/cgi-bin/service/get_corp_token?suite_access_token=" + suiteAccessToken)
                .body(JSONUtil.toJsonStr(params))
                .execute().body();
        JSONObject accessInfo = JSONUtil.parseObj(respStr);
//        log.info("获取获客助手接口凭证： REQ === {} ", params);
//        log.info("获取获客助手接口凭证： RESP === {} ", accessInfo);
        apiConfig.setAcqAccessToken(accessInfo.getStr("access_token"));
        apiConfig.setAcqTokenExpireIn(accessInfo.getInt("expires_in"));
    }

    public void updateContactAccessToken(WeComApiConfigDO apiConfig, String suiteAccessToken) {
        if (StrUtil.isEmpty(suiteAccessToken) || apiConfig.getContactAuthStatus() == 0) {
            return;
        }
        Map<String, Object> params = new HashMap<>();
        params.put("auth_corpid", apiConfig.getCorpId());
        params.put("permanent_code", apiConfig.getContactPermanentCode());
        String respStr = HttpUtil.createPost("https://qyapi.weixin.qq.com/cgi-bin/service/get_corp_token?suite_access_token=" + suiteAccessToken)
                .body(JSONUtil.toJsonStr(params))
                .execute().body();
        JSONObject accessInfo = JSONUtil.parseObj(respStr);
//        log.info("获取通讯录接口凭证： REQ === {} ", params);
//        log.info("获取通讯录接口凭证： RESP === {} ", accessInfo);
        apiConfig.setContactAccessToken(accessInfo.getStr("access_token"));
        apiConfig.setContactTokenExpireIn(accessInfo.getInt("expires_in"));
    }
}